home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / bigscrolling / bigscrolling.p < prev    next >
Encoding:
Text File  |  2000-06-23  |  11.2 KB  |  289 lines

  1. {
  2.     File:        BigScrolling.p
  3.  
  4.     Contains:    ScrollBars are limited in their range to a signed 16-bit number (+/- 32768).
  5.                 This project demo's one way to work around this limitation.
  6.  
  7.     Written by:     
  8.  
  9.     Copyright:    Copyright © 1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 7/16/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. }
  24.  
  25. unit BigScrolling;
  26.  
  27. interface
  28. uses
  29.     Traps,Windows,Controls,TextUtils,Fonts;
  30.  
  31. type
  32.  
  33. { We attach this record, which contains longint values for the minimum, maximum and value of the control, to the }
  34. { control's refcon.  Our "MyGetCtlValue" and "MySetCtlValue" look for a pointer to this record in the control's }
  35. { refcon so we can find it. }
  36.  
  37.     bigValues = record
  38.             bigMin: longint;
  39.             bigMax: longint;
  40.             bigValue: longint;
  41.         end;
  42.     bigValuesPtr = ^bigValues;
  43.  
  44. procedure InitializeApplication;
  45. procedure TerminateApplication;
  46. procedure CloseAppWindow (theWindow: WindowPtr);
  47. procedure DrawWindow (theWindow: WindowPtr; drawingPort: grafPtr; printing: BOOLEAN; isActive: BOOLEAN);
  48. procedure DoContentClick (window: WindowPtr; event: EventRecord);
  49. procedure MySetCtlValue (theControl: ControlHandle; theValue: longint);
  50. function MyGetControlValue (theControl: ControlHandle): longint;
  51. procedure FixMyCtlValue (theControl: ControlHandle);
  52.  
  53. implementation
  54.  
  55. var
  56.     ourScrollBar: ControlHandle;
  57.  
  58. { MySetCtlValue takes a longint control value.  It stuffs the value into the bigValues record pointed to by the }
  59. { control refcon and normalizes it into a 0..32767 range for the control's actual value (signed integer).  This}
  60. { is obviously a loss of precision, but it's OK because this is only used for displaying the thumb position by}
  61. { the Control Manager. }
  62.  
  63. procedure MySetCtlValue (theControl: ControlHandle; theValue: longint);
  64.  
  65.     var
  66.         extendedValue: extended;
  67.         intValue: integer;
  68.         ourValues: bigValuesPtr;
  69.  
  70.  
  71.     begin
  72.         ourValues := bigValuesPtr(GetControlReference(theControl));
  73.         with ourValues^ do
  74.             begin
  75.  
  76. { We have to pin the value to our minimum and maximum values so we don't underflow or overflow. }
  77.  
  78.                 if theValue > bigMax then
  79.                     bigValue := bigMax
  80.                 else if theValue < bigMin then
  81.                     bigValue := bigMin
  82.                 else
  83.                     bigValue := theValue;
  84.  
  85. { To normalize, we subtract the bigMin from bigMax to get the control's range of values.  Dividing that interval}
  86. { by 32767 tells us how much our longint control value has to change before the real control value changes by one. }
  87. { For example, if our range is 0 to 65535, the real control value moves by one every time our value changes}
  88. { by two.  Once we have that interval, we divide our real value by it to get a normalized value. }
  89. { (our Value)/(interval / 32767) is the same as (ourValue * 32767) / interval, which is the form we use.}
  90. { 32767 is expressed as 32767.0 so extended calculations are done, to avoid overflow even for really big}
  91. { values. }
  92.  
  93.                 extendedValue := (((bigValue - bigMin) * 32767.0) / (bigMax - bigMin));
  94.                                                                                         { this will always be between 0 and 32767 }
  95.                 intValue := round(extendedValue);                                            { explicitly truncate to integer }
  96.                 SetControlValue(theControl, intValue);
  97.             end;    {with}
  98.     end;
  99.  
  100. { FixMyCtlValue resets bigValue to something resembling the actual control value, for those occasions when the }
  101. { Control Manager drags the thumb for you and resets the value based on the min and max fields in the control }
  102. { record.   It reverses the MySetCtlValue calculation to get an approximation of where the big control value is for }
  103. { the place where the user dragged the thumb; this is the best we can do because the scroll bar is never going to get}
  104. { the resolution of the values (you can't have a scroll bar one million pixels tall).  In the special case that they're at }
  105. { the very bottom of the scroll bar, we set the value to bigMax so it's more consistent with what users expect. }
  106. { Since each step in the real control value represents many steps in ours, if the new control value is the same as }
  107. { where the old bigValue would put it, we don't change it.  This means if you click on the thumb but don't move it, }
  108. { the control value doesn't change. We have to use extended arithmetic here as well to avoid round-off errors.}
  109.  
  110. procedure FixMyCtlValue (theControl: ControlHandle);
  111.  
  112.     var
  113.         allegedValue, intValue: integer;
  114.         oldBigValue: longint;
  115.         ourValues: bigValuesPtr;
  116.         extendedValue, newBigExtended: extended;
  117.  
  118.     begin
  119.         ourValues := bigValuesPtr(GetControlReference(theControl));
  120.         allegedValue := GetControlValue(theControl);
  121.         with ourValues^ do
  122.             begin
  123.                 oldBigValue := bigValue;
  124. { To reverse the calculation, we divide the interval of possible values by the maximum real control value, then multiply }
  125. { that by the value the Control Manager has.  Since the minimum might not be zero, we add it in as well. }
  126.  
  127.                 newBigExtended := (((((bigMax - bigMin) / 32767.0)) * allegedValue) + bigMin);
  128.                 bigValue := round(newBigExtended);
  129.  
  130.                         { Now, if that new bigValue has the same CtlValue as the old bigValue, restore the old one. }
  131.  
  132.                 extendedValue := (((oldBigValue - bigMin) * 32767.0) / (bigMax - bigMin));
  133.                                                                                         { this will always be between 0 and 32767 }
  134.                 intValue := round(extendedValue);                                            { explicitly truncate to integer }
  135.                 if intValue = allegedValue then
  136.                     bigValue := oldBigValue
  137.                 else if allegedValue = 32767 then
  138.                     bigValue := bigMax                                        { pin to bottom only if not changing value otherwise }
  139.  
  140.             end;   { with }
  141.     end;              { procedure }
  142.  
  143. procedure InitializeApplication;
  144.     var
  145.         theWindow: WindowPtr;
  146.         newValuesPtr: bigValuesPtr;
  147.  
  148.     begin
  149.  
  150. { Create and show our window }
  151.  
  152.         theWindow := GetNewWindow(128, nil, Pointer(-1));        { window is invisible in the WIND resource }
  153.         ourScrollBar := GetNewControl(128, theWindow);
  154.         newValuesPtr := bigValuesPtr(NewPtr(sizeof(bigValues)));
  155.         newValuesPtr^.bigMin := 0;                                                { an arbitrary minimum }
  156.         newValuesPtr^.bigMax := 1500000;                                    { an arbitrary maximum }
  157.         SetControlReference(ourScrollBar, longint(newValuesPtr));            { put a pointer to the record in the control's refCon}
  158.         MySetCtlValue(ourScrollBar, 1000000);                            { an arbitrary initial value }
  159.         ShowWindow(theWindow);
  160.  
  161.     end;
  162.  
  163. procedure TerminateApplication;                                            { Called by Sample.p -- not needed in this unit }
  164.     begin
  165.     end;
  166.  
  167. procedure CloseAppWindow (theWindow: WindowPtr);
  168.  
  169.     begin
  170.         DisposePtr(Ptr(GetControlReference(ourScrollBar)));                        { Dispose of our bigValues structure }
  171.         CloseWindow(theWindow);                                                    { and close the window. }
  172.     end;
  173.  
  174. procedure DrawTheValue (theValue: longint);
  175.  
  176.     var
  177.         tempString: Str255;
  178.         myRect: Rect;
  179.     begin
  180.         SetRect(myRect, 40, 40, 150, 100);                                { an arbitrary sized rectangle to draw in }
  181.         EraseRect(myRect);
  182.         MoveTo(50, 50);
  183.         TextFont(kFontIDGeneva);
  184.         NumToString(theValue, tempString);                                    { turn the value into a string }
  185.         DrawString(tempString);
  186.     end;
  187. { MyGetCtlValue returns our longint value from the bigValues record attached to the refcon.  There's no calculation}
  188. { to do here, because MySetCtlValue and FixMyCtlValue does all that for us. }
  189.  
  190. function MyGetControlValue (theControl: ControlHandle): longint;
  191.  
  192.     var
  193.         ourValues: bigValuesPtr;
  194.  
  195.     begin
  196.         ourValues := bigValuesPtr(GetControlReference(theControl));
  197.         MyGetControlValue := ourValues^.bigValue;
  198.     end;
  199.  
  200. { This procedure is called by the shell-like Sample.p file to draw windows.  Sample.p sets "printing" to TRUE if }
  201. { it's printing (though this snippet doesn't print), so we ignore that.  We also ignore "isActive" because in this }
  202. { program, we have one window and it's always active, and we ignore "drawingPort" because it's NIL unless we're }
  203. { printing, in which case it's the printing grafPort.  All we have to do in this routine is call DrawControls to draw}
  204. { our scroll bar, then call DrawTheValue to provide an integer representation of it. }
  205.  
  206. procedure DrawWindow (theWindow: WindowPtr; drawingPort: grafPtr; printing: boolean; isActive: boolean);
  207.     var
  208.         oldPort: grafPtr;
  209.         theValue: longint;
  210.     begin
  211.         GetPort(oldPort);
  212.         SetPort(theWindow);
  213.         DrawControls(theWindow);
  214.         theValue := MyGetControlValue(ourScrollBar);
  215.         DrawTheValue(theValue);
  216.         SetPort(oldPort);
  217.     end;
  218.  
  219. { NonThumbAction is the action routine we pass to TrackControl when the user clicks on the scroll arrows or page}
  220. { region of the scroll bar.  We add arbitrary values to the control value, then call MySetCtlValue to change the }
  221. { control's value and redraw it.  We then draw the value after retrieving it again -- we retrieve it with MyGetControlValue }
  222. { just in case our last change would have gone under the minimum or over the maximum.  MySetCtlValue prevents that, }
  223. { and we get the corrected value before displaying it. }
  224.  
  225. procedure NonThumbAction (theControl: ControlHandle; partCode: integer);
  226.  
  227.     var
  228.         ourValue: longint;
  229.  
  230.     const
  231.         arrowUpAmount = -1;
  232.         arrowDownAmount = 1;
  233.         pageUpAmount = -1000;
  234.         pageDownAmount = 1000;
  235.  
  236.     begin
  237.         ourValue := MyGetControlValue(theControl);
  238.  
  239.         case partCode of
  240.             kControlUpButtonPart: 
  241.                 ourValue := ourValue + arrowUpAmount;
  242.             kControlDownButtonPart: 
  243.                 ourValue := ourValue + arrowDownAmount;
  244.             kControlPageUpPart: 
  245.                 ourValue := ourValue + pageUpAmount;
  246.             kControlPageDownPart: 
  247.                 ourValue := ourValue + pageDownAmount;
  248.         end;
  249.         MySetCtlValue(theControl, ourValue);
  250.         DrawTheValue(MyGetControlValue(theControl));            { in case it got pinned to the minimum or maximum }
  251.     end;
  252.  
  253. { Sample.p calls DoContentClick when it finds a click in the content region of an app window (that's us).  Since all }
  254. { we care about is the scroll bar, we call FindControl and then TrackControl, passing NonThumbAction if they click}
  255. { in the scroll bar (but not in the thumb), and passing NIL if they click in the thumb.  If they dragged the thumb, we}
  256. { use FixMyCtlValue to repair the bigValue in our private record on the refcon. }
  257.  
  258. procedure DoContentClick (window: WindowPtr; event: EventRecord);
  259.     var
  260.         thePartCode, theNewPartCode: integer;
  261.         ourLocalPoint: Point;
  262.         ourControl: ControlHandle;
  263.         oldPort: grafPtr;
  264.  
  265.     begin
  266.         GetPort(oldPort);
  267.         SetPort(window);
  268.         ourLocalPoint := event.where;
  269.         GlobalToLocal(ourLocalPoint);
  270.         thePartCode := FindControl(ourLocalPoint, window, ourControl);
  271.         case thePartCode of
  272.             0: 
  273.                 ;
  274.                      { we get and ignore zero if they mouse up outside the part they mouse down-ed in }
  275.             kControlUpButtonPart, kControlDownButtonPart, kControlPageUpPart, kControlPageDownPart: 
  276.                 theNewPartCode := TrackControl(ourControl, ourLocalPoint, NewControlActionProc(ControlActionProcPtr(@NonThumbAction)));
  277.             kControlIndicatorPart: 
  278.                 begin
  279.                     theNewPartCode := TrackControl(ourControl, ourLocalPoint, nil);
  280.                     FixMyCtlValue(ourControl);                { change bigValue to match to where they moved the thumb }
  281.                     DrawTheValue(MyGetControlValue(ourControl));
  282.                 end;
  283.         end;                { case thePartCode of }
  284.         SetPort(oldPort);
  285.  
  286.     end;
  287.  
  288.  
  289. end.